#include "utils.h"


static int DecodeUTF8(const char*& ptr)
{
	const unsigned char *str = reinterpret_cast<const unsigned char*>(ptr);
	unsigned char ch = *str++;
	if(ch <= 0x7F) {
		ptr++;
		return ch;
	}
	unsigned int cp;
	size_t count;
	if(ch >= 0xFE) {
		//error, illegal byte
		return -1;
	} else if(ch >= 0xFC) {
		count = 5;
		cp = ch & 0x01;
	} else if(ch >= 0xF8) {
		count = 4;
		cp = ch & 0x03;
	} else if(ch >= 0xF0) {
		count = 3;
		cp = ch & 0x07;
	} else if(ch >= 0xE0) {
		count = 2;
		cp = ch & 0x0F;
	} else if(ch >= 0xC0) {
		count = 1;
		cp = ch & 0x1F;
	} else {
		//error, illegal byte
		return -1;
	}
	if(cp == 0) {
		//error, non-minimal encoding
		return -1;
	}

	for(size_t i = 0; i < count; i++) {
		ch = *str++;
		if((ch & 0xC0) != 0x80)
			return -1; //illegal continuation byte
		cp = (cp << 6) | (ch & 0x3F);
	}
	ptr = reinterpret_cast<const char*>(str);

	return cp;
}

std::wstring Utf8To16(const std::string& utf8)
{
	return Utf8To16(utf8.c_str());
}

std::wstring Utf8To16(const char *utf8)
{
	std::wstring out;
	out.reserve(64);
	for(;;) {
		//decode one character
		int cp = DecodeUTF8(utf8);
		if(cp == 0)
			break;
		if(cp < 0)
			return L"";

		if(cp <= 0xFFFF) {
			out.push_back(cp);
		} else if(cp <= 0x10FFFF) {
			cp -= 0x10000;
			out.push_back(0xD800 | (cp >> 10));
			out.push_back(0xDC00 | (cp & 0x3FF));
		}
	}
	return out;
}

std::string Utf16To8(const wchar_t *utf16)
{
	std::string out;
	out.reserve(64);
	for(;;) {
		wchar_t val = *utf16++;
		if(val >= 0xD800 && val <= 0xDFFF) {
		} else {
			//normal encode, 1-3 bytes
			if(val <= 0x7F) {
				//1
				if(val == 0)
					return out;
				out.push_back((char)val);
			} else if(val <= 0x7FF) {
				//2
				out.push_back(0xC0 | (val >> 6));
				out.push_back(0x80 | (val & 0x3F));
			} else {
				//3
				out.push_back(0xE0 | (val >> 12));
				out.push_back(0x80 | ((val >> 6) & 0x3F));
				out.push_back(0x80 | (val & 0x3F));
			}
		}
	}
}

int ConnectToHost(const char *name, const char *port)
{
	addrinfo *info;
	if(getaddrinfo(name, port, NULL, &info))
		return INVALID_SOCKET;
	for(; info; info = info->ai_next) {
		int fd = (int)socket(info->ai_family, SOCK_STREAM, 0);
		if(!connect(fd, info->ai_addr, info->ai_addrlen))
			return fd;
#ifdef WIN32
		closesocket(fd);
#else
		close(fd);
#endif
	}
	return INVALID_SOCKET;
}

int ConnectToHostList(std::list<std::string>& list, const char *port)
{
	for(std::list<std::string>::iterator it = list.begin(); it != list.end(); ++it) {
		std::string name = *it;
		size_t ofs = name.find(':');
		int fd;
		if(ofs < name.size()) {
			fd = ConnectToHost(name.substr(0, ofs).c_str(), name.substr(ofs+1).c_str());
		} else {
			fd = ConnectToHost(name.c_str(), port);
		}
		if(fd != INVALID_SOCKET)
			return fd;
	}
	return INVALID_SOCKET;
}
